
\title{pyNastran Developer Document \\
\small http://code.google.com/p/pynastran/ }
\author{Steven P. Doyle\\
{\small mesheb82@gmail.com}
}

\maketitle

\begin{abstract}
This document is intended to be a reference guide for users.
\end{abstract}

Copyright \copyright\ Steve P. Doyle 2011-2012
\newpage

\tableofcontents
\newpage

\section{Brief Project Overview}
  \subsection{Introduction}
      Since the 1960's NASTRAN (NASA Structural ANalysis) has been used to solve structural/thermal/aerodynamic/dynamics/etc. problems.  The file formats were originally developed by MSC for a product now called MSC Nastran.  There have been many spinoff version of NASTRAN that have been created based on the 2001 source code release of MSC Nastran in 2002 after settlement with the FTC (Federal Trade Commisson).  There is now NX Nastran and NEi Nastran, which are developed independently.
     
     pyNastran is at it's core an API (Application Programming Interface) to the legacy formats used by Nastran.  These files include the BDF, F06, OP2, OP4, and PCH files.  Other code has been added to pyNastran in order to drive development of the software in order to be able to solve other engineering problems.  For example, Code\_Aster, an open-source finite element code developed by the EDF (Electricity of France), has a Nastran to Code\_Aster converter that is in development.  The development has helped to define the API for the loads in order to be able to extract them in a way that makes sense.  However, this is not the focus of the software.

  \subsection{Target Audience}
      pyNastran's target audience are users of Nastran and therefore are expected to be familiar with the software.  This has greatly reduced the necessity of documenting every variable exhasutively as users can easily reference existing Nastran documentation. The BDF file has roughly 700 cards availble to a user with 200 being currently supported by pyNastran.  The majority of the cards, defined as separate Python classes, are not documented.  However, the Quick Reference Guide (QRG) defines each input to the card.  A user with the QRG should have little effort in understanding what the various objects do.  However, for convenience, it's still good to document variables.

     pyNastran's target audience largely uses MATLAB, a matrix based programming language, and typically has little experience with general programming.  There are also users that know Python, but have never used a class or a dictionary, which makes an API seems complicated.  Additionally, while not difficult to build, the Cython OP4 reader, which requires a C complier, has been a source of frustration for many users.  That said, most operations should be relatively easy to accomplish.


\section{Getting Started: Code Formatting}
 \subsection{PEP-8}
     pyNastran is attempting to implement Python's PEP-8 formatting standard after not following it for multiple versions.  It's a work in progress to upgrade the code, so try to follow it.  The biggest challenge has been to come up with a clear way to reference methods and very similar variable names.  For example, the element.Area() method uses the element.area variable in the case of the CQUAD4, but references element.areas in the case of a multi-segmented CBAR.  It's recommended that developers use Spyder with pylint integration to help with identifying PEP-8 deficencies.

    Python PEP-8:
     
     http://www.python.org/dev/peps/pep-0008/

     1.  Use spaces (not tabs) when indenting code

     2.  Try to limit code to 80-characters width

     3.  Files are named as\_follows.py

     4.  Classes are named AsFollows

     4.  Functions are are named as\_follows() and private functions are named \_as\_follows()

     5.  Variable names\_have\_embedded\_underscores or without any (e.g. self.elements).  A private variable would look \_similar\_to\_a\_function

     6.  Don't use import *

     7.  Break the rules if it's clearer

     8.  Document functions/classes/variables

 \subsection{Additional Guidelines}
     1.  pyNastran liberally uses classes (it has too many!), so try not to write more classes than necessary.  Functions are often better to avoid having extra functions in objects the user has immediate access to (imagine having 100 functions to sift through) in an IDE.  Watch http://www.youtube.com/watch?v=o9pEzgHorH0 for a better explanation.  Again, it's a work in progress.

     2.  When writing classes, group similar classes in the same file to reduce having to search for a class across multiple files (very common in Java).

     3.  When deprecating functions, throw a warning using the Python warnings module.  It's easier to make sure the rest of the code isn't using it this way.  This is not nearly as important if it's not a function the user is likely to interact with.

 \subsection{Documentation}
     pyNastran uses Doxygen for docstrings.  Doxygen is nice because it allows LaTeX to be embedded in the documentation and HTML docs are easily generated.  Sphinx is another documentation format, but no investigation has been done into how the quality of the documentation would change.  It is known that Sphinx also allows LaTeX and that it is much more commonly used.

    Some guidelines for Doxygen:

     1.  \@param  in\_variable\_name a description goes here

     2.  \@retval out\_variable\_name another description

     3.  \@see    look at AsFollows class or as\_follows function

     4.  \@note   a message

     5.  \@warning a warning

     6.  \@todo   something that's not done
     
     

\section{Getting Started: BDF Reading}
 \subsection{{\tt bdf.py}:  Introduction}
     {\tt bdf.py} controls the model object that is instantiated with      {\tt model=BDF()} the {\bf \tt \_\_init\_\_} method is called when {\tt model = BDF()} is run.  The file used to be very large and was split (in a non-standard way) into multiple files that allowed for simpler development.  The {\bf BDFReader}, {\bf GetMethods}, {\bf AddMethods}, {\bf WriteMesh},
     {\bf CardMethods}, and {\bf XrefMesh} classes are basically bags of functions for the "model" object.
     
     The {\tt cardsToRead} variable limits the cards that pyNastran processes and     can be modified by the user in order to fix bugs or make their code run faster.
     
     Moving onto {\bf \tt \_init\_solution} sets a series of alternate names for Nastran solution types.  For example, a solution 101 is a static solution (no acceleration) and will calculate the displacements of the system $[K]\{x\} = \{F\} $.  You can then extract stresses and strains.  Other solution numbers solve different equations.
     
     In {\bf \tt \_init\_structural\_defaults}, {\bf \tt \_init\_aero\_defaults}, {\bf \tt \_init\_thermal\_defaults} the structural, aerodyanmic, and thermal card holders (e.g. model.materials) are defined as dictionaries.

     Finally, the {\bf readBDF} method is defined.  There are three sections to a BDF/DAT/NAS file.  BDF (Bulk Data File) is the file format used by MSC Nastran and the DAT (data?) file is used by NX Nastran.  NAS (Nastran) is also used.

     The first section is the "Executive Control Deck" and contains a "SOL 101" or "SOL 103" or "SOL STATIC" depending on the solution type.  It ends when the "CEND" marker is found. Then the "Case Control Deck" is read.  Here, general solution information is listed, such  as what outputs do you want and what loads are applied (not all loads in the file are necessarily applied).  Finally this section defines one or more subcases, which are different load combinations.  The last section is the "Bulk Data Deck".  This section stores 99% of the file and this section introduces very specific formatting restrictions for "cards".
     
     A basic model will be made of nodes, elements, properties, and materials.  For example, for a square plate made of steel model GRID cards are used for the nodes, CQUAD4 or CTRIA3 cards are used for the elements (the C generally indicates the card is an element so quadrilateral element and triangular element).  The element has a property (PSHELL) that defines the thickness.  Similarly, properties generally start with P.  Finally,  the property references a material (MAT1) that defines the material as steel.  INCLUDE cards may also be used to add additional files into the BDF.
     
 \subsection{{\tt bdf.py}: Card Formatting}
     A "card" is at most 72-characters wide.  Comments may follow the card if a \$ sign is used.
     
     The standard card is called small field format (single precision) and has 9 fields defined per line, each with 8-characters and are fixed width.  Multiline cards are implied by leaving 8 spaces at the beginning of the following line.  Alternatively, a + sign may be used in the first 8 spaces.
     
     The large field format (double precision) card uses a 1*8+4*16 to reach the 72-character width instead of 1*8+8*8 characters.  If the first line of a card is double precision, a * follows the card name, so all card names are 7-characters or less.  If the second line of a card is double precision, a * begins the line.  A single line of a small field formatted takes exactly two lines to write if large field format is used.
     
     The CSV (comma separated value) format is similar to small field format.  It's less picky then the 8-character format, but much harder to read.  It is still subject to the 9 fields per line restriction.  If a CSV card has a * on it, the card becomes a large field CSV formatted card and may have only 5 fields on the line (including the blank field).
     
     Although introduced as separate types of cards, small field format and large field format may be mixed and matched.  However, this only occurs for hand-edited BDFs.  There's also a difficult to understand format known as a "continutation card".  This uses values from previous cards and is basically a for loop.  Hundreds of cards may be defined in just a few lines.
     
 \subsection{{\tt bdf.py}: Parsing}
     A basic card is a GRID card.  Once parsed, a standard grid card will have fields of {\tt ['GRID', nodeID, coord, x, y, z]}.  This section will discuss how a card is parsed.  
     
     The {\bf readBDF} method must generalize the way files are opened because INCLUDE files may be used.  Once the Executive and Case Control Decks are read (using simple while loops), the {\bf \tt \_read\_bulk\_data\_deck} method is called.
     
     This method ({\bf \tt \_read\_bulk\_data\_deck}) keeps looping over the file as long as there are files opened (an INCLUDE file side effect) and calls:
     {\tt (rawCard, card, cardName) = self.\_get\_card(debug=False)}.  cardName is just the card's name, while rawCard is the full, unparsed card.  card is the card parsed into fields as a card object, which is basically a list of {\tt fields=['GRID', nodeID, coord, x, y, z]}.

     The basic idea of the {\bf \tt self.\_get\_card()} method(see {\tt bdf\_CardMethods.py}) is to make a {\tt self.linesPack}, which is a list of 1500 lines that are stored in memory.  When a new line is added to {\tt self.linesPack}, it is first stripped of comments in order to simplify parsing.  If the linesPack is empty or 50 blank lines are found, the code assumes an infinite loop has been entered and closes the file.  If additional files are open, the linesPack from the previous file will be used (INCLUDE file case).
     
     Now that we have 1500 lines in linesPack, we must call:  {\bf \tt (i, tempcard) = self.\_get\_multi\_line\_card(i, tempcard)} to get the card.  tempcard starts out as the first line in the card and afterwards contains all lines of the card. tempcard will eventually become rawCard.  It's important to note the tempcard passed into {\bf \tt \_get\_multi\_line\_card} is a 72-character string (generally) and the tempcard output is a list of 8-character (or 16) width fields.  Why: the original data isn't needed, so the variable is reused.
     
     {\bf \tt \_get\_multi\_line\_card} will search through the linesPack and try to end the card by looking for a non-whitespace character in the first character position (all cards must have {\tt field[0]} justified).  If a * is found, it's double precision, if not it's small field.  Additionally, if a ',' is found it's CSV format.  So the formats are:

       1. small field,

       2. small field CSV

       3. large field

       4. large field CSV
     
     Once the format of the line is known, it's an easy process to split the card (see {\bf self.processCard} in {\tt bdf\_CardMethods.py}) and turn it into a {\bf BDFCard} object.  Note that the first field in any line beyond the first one must be blank and is ignored.  This prevents cards defined in small field and large field to have the same value defined in different positions of the list.
     
     Finally, as Nastran is very specific in putting a decimal on float values, it's easy to parse field values into their proper type dynamically.  This is especially important when a field may be defined as an integer, float, a string, or be left blank and the variable is different depending on variable type.  Strings, must being with alphabetical characters (A, B, C) and are case insensitive, which is why a "GRID" card is called a "GRID" card and not a "grid" card.
     
     
 \subsection{{\tt bdf.py}: Card Object}
     A {\bf BDFCard} object is basically a list of fields of {\tt ['GRID', nodeID, coord, x, y, z]}  with methods to get the 1st entry (nodeID) as card.field(1) instead of {\tt fields[1]} for a list.  A card object is useful for setting defaults.  The x, y, and z values on the GRID card have defaults of 0.0, so {\tt card.field(3,0.0)} may be used to get the x coordinate. Finally, {\tt card.fields(3,5,[0.,0.,0.])} may be used to get xyz and set the defaults in a single step.  Additionally, the card object is useful when parsing "continuation cards", but is typically disabled.  
     
     After an excessively long branch of cardNames in readBDF, the card object is turned into a GRID, CTRIA3, CQUAD4, PSHELL, MAT1 or any of 200 other card types.  There are roughly as many nodes as there are elements, which make up roughly 95% of the cards in large models.  The difference in a large model and a small model, is the discritization and will change nodes, elements, loads, and constraints.  Loads and constraints are applied to only small portions of the model and (generally) only the boundary of a model.  The number of propertie and materials is very likely the same.
   
     Most cards are stored in a dictionary based on their integer ID.  IDs may be used only once, but if a card is exactly duplciated, it is still valid.

 \subsection{{\tt shell.py}: CQUAD4 Object}
     In {\tt bdf/cards/elements/shell.py}, the CQUAD4 is defined.
     
     The CQUAD4 is a shell-type element and must reference a PSHELL (isotropic property) or a PCOMP (composite property) card.  An example of an isotropic material is steel or aluminum and a composite material would be fiberglass or layers of carbon fiber/epoxy resin at layed up at different angles.
     
     The PSHELL may reference MAT1 (isotropic material) cards, while the PCOMP card may reference MAT1 or MAT8 (orthotropic material) cards.  An orthotropic material is stronger in longitudinally than laterally (e.g. fibers are oriented unidirectionally in a carbon fiber composite).
     
     The CQUAD4 class inherits from the {\bf QuadShell} class which defines common methods to the various QUAD-type cards.  There are additional QUAD element with different numbers of nodes (8-CQUAD8, 9-CQUAD) and the CQUADR and CQUADX are axi-symmetric versions of the CQUAD4, and CQUAD8 respectively.  However, the {\bf Area()}, {\bf Mass()}, {\bf Density()}, etc. methods are calculated in the the same way for each card (although the axi-symmetric cards return mass per unit theta).  The last thing to note is {\bf rawFields} and {\bf reprFields} are very important to how the code integrates.
     
     {\bf rawFields} is used to check if a duplicated card is the same as another card and is also used for testing.  After reading and writing, reading back in, writing back out, reading back in, if the fields are the 
     same, then there's likely no error in reading a card (fields can still be missed while reading, so it's not perfect).  rawFields returns a list of the fields (similar to the list-esque card object from before).
     
     {\bf reprFields} is analogous to the {\bf \tt \_\_repr\_\_()} method, and is an abbreviated way to write the card.  For example, the T1, T2, T3, and T4 values (thickness at nodes 1, 2, 3, 4) are generally 0.0 and instead are set at an elemental level using the PSHELL card.  If these fields were printed, the CQUAD4 card would be a two line card instead of a one line card.  {\bf reprFields} is used instead of {\bf \tt \_\_repr\_\_()} in order to be able to write the card in large field or small field format.  Defaults are generally not written by the {\bf \tt \_\_repr\_\_()} method, but are written for certain fields (e.g. the xyz fields on the GRID card).
    
     To get the CQUAD4, with an element ID of 1000, you would type:
       {\tt elem = model.elements[1000]} or
       {\tt elem = model.Element(1000)} to use the function.
     
     Then to print the card, type:
       {\tt print elem}
     to see the Nastran formatted card.  The {\bf \tt \_\_repr\_\_()} method is defined in {\tt bdf/cards/baseCard.py} the {\bf BaseCard} class (which is used by the {\bf Element} class also defined in {\tt baseCard.py}).

 \subsection{{\tt shell.py}: Cross-Referencing the CQUAD4 Object}
     Previously, it was mentioned that the square plate model built with quads and triangles had a thickness and a material.  The nodes of the elements also have positions.  The nodes further be defined in a rectangular, cylindrical, or spherical coordinate system, so to get the mass of an element is actually quite involved.  Creating a function to access the mass becomes possible without passing the entire model object around to every function through the use of cross-referencing.
     
     Cross Referencing takes a CQUAD4 card and replaces the GRID references with actual GRID cards.  The GRID cards in turn reference two COORDx (CORD1R, CORD2R, CORD1C, COR2DC, CORD1S, CORD2S) cards, which also may reference two CORDx cards.  The CQUAD4 references a PSHELL or PCOMP card.  The PSHELL references a single MAT1 card, and as mentioned before the PCOMP card may reference one or more MAT1/MAT8 cards.  In order to calculate something simple like the mass of the CQUAD4 requires the formula:
     
     {\begin{equation} m=A \left( t\rho + \frac{nsm}{A} \right) \end{equation} } for a PSHELL or:

     {\begin{equation} m=A \left( \sum_{i=0}^{i=1}{t\rho} + \frac{nsm}{A} \right)  \end{equation} } for a PCOMP.
     

     By using classes and functions, it's easy to just call the {\tt element.MassPerArea()} method and get the proper data to apply the formula.  Similarly, the {\tt element.Area()} method calls the {\tt node.Position()} method to get the node in the global XYZ coordinate frame and can then find the area using vectors in a 3D space:
     {\begin{equation} A=\frac{1}{2} | (n_1-n_3) \times (n_2-n_4) | \end{equation} }
     (see http://en.wikipedia.org/wiki/Quadrilateral).
 
 
 \subsection{{\tt crossReference.py}: Cross-Referencing Process}
     Cross referencing must first be done on the coordinate cards.  Then, once they're done, the nodes are cross referenced.  Once this is done, the coordinate systems may be resolved (CORD1x cards reference GRID cards).  Then elements, properties, materials, loads, boundary conditions, aerodynamic cards, thermal, dynamic, etc. cards are mapped.  The order doesn't matter, but CORD1x cards and GRID cards must be mapped first.
     
     Cross Referencing is performed by looping over the card objects and calling the {\bf \tt card.crossReference()} method.  This will setup all cross-referencing and a full list of the status of various cards is listed in {\tt bdf\_crossReferencing.txt}.

 \subsection{{\tt writeMesh.py}: Writing the BDF}
     The BDF is written by looping through all the objects and calling the {\bf \tt card.\_\_repr\_\_()} method by typing {\bf \tt str(card)}.
     Currently, only small field format is supported for writing.  The list from {\bf \tt reprFields()} is passed into {\tt fieldWriter.py}'s function {\bf \tt printCard(listObj)} and it dynamically figures out how to write the card based on the data type.  For float values, the highest precision 8-character width field will be used even if it uses Nastran's strange syntax of "1.2345+8" to represent a more standard "1.2345e+08".